/*-------------------------------------------------------------------------
 *
 * expandeddatum.c
 *	  支持 "扩展" 值表示的函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/expandeddatum.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "utils/expandeddatum.h"
#include "utils/memutils.h"

/*
 * DatumGetEOHP
 *
 * 给定一个扩展对象引用的 Datum，提取指针。
 *
 * 这有点繁琐，因为指针可能未对齐；比较 VARATT_EXTERNAL_GET_POINTER()。
 */
ExpandedObjectHeader *
DatumGetEOHP(Datum fc_d)
{
	varattrib_1b_e *fc_datum = (varattrib_1b_e *) DatumGetPointer(fc_d);
	varatt_expanded fc_ptr;

	Assert(VARATT_IS_EXTERNAL_EXPANDED(fc_datum));
	memcpy(&fc_ptr, VARDATA_EXTERNAL(fc_datum), sizeof(fc_ptr));
	Assert(VARATT_IS_EXPANDED_HEADER(fc_ptr.eohptr));
	return fc_ptr.eohptr;
}

/*
 * EOH_init_header
 *
 * 初始化扩展对象的公共头。
 *
 * 这封装的主要内容是初始化 TOAST 指针。
 */
void EOH_init_header(ExpandedObjectHeader *fc_eohptr,
				const ExpandedObjectMethods *fc_methods,
				MemoryContext fc_obj_context)
{
	varatt_expanded fc_ptr;

	fc_eohptr->vl_len_ = EOH_HEADER_MAGIC;
	fc_eohptr->eoh_methods = fc_methods;
	fc_eohptr->eoh_context = fc_obj_context;

	fc_ptr.eohptr = fc_eohptr;

	SET_VARTAG_EXTERNAL(fc_eohptr->eoh_rw_ptr, VARTAG_EXPANDED_RW);
	memcpy(VARDATA_EXTERNAL(fc_eohptr->eoh_rw_ptr), &fc_ptr, sizeof(fc_ptr));

	SET_VARTAG_EXTERNAL(fc_eohptr->eoh_ro_ptr, VARTAG_EXPANDED_RO);
	memcpy(VARDATA_EXTERNAL(fc_eohptr->eoh_ro_ptr), &fc_ptr, sizeof(fc_ptr));
}

/*
 * EOH_get_flat_size
 * EOH_flatten_into
 *
 * 用于调用扩展对象的“方法”的便捷函数。
 */

Size EOH_get_flat_size(ExpandedObjectHeader *fc_eohptr)
{
	return fc_eohptr->eoh_methods->get_flat_size(fc_eohptr);
}

void EOH_flatten_into(ExpandedObjectHeader *fc_eohptr,
				 void *fc_result, Size fc_allocated_size)
{
	fc_eohptr->eoh_methods->flatten_into(fc_eohptr, fc_result, fc_allocated_size);
}

/*
 * 如果 Datum 代表 R/W 扩展对象，将其更改为 R/O。
 * 否则返回原始 Datum。
 *
 * 调用者必须确保 datum 是非空的 varlena 值。通常通过 MakeExpandedObjectReadOnly() 调用，这会进行检查。
 */
Datum MakeExpandedObjectReadOnlyInternal(Datum fc_d)
{
	ExpandedObjectHeader *fc_eohptr;

	/* 如果不是可读写的扩展对象指针，则无事可做 */
	if (!VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(fc_d)))
		return fc_d;

	/* 现在可以安全地提取对象指针 */
	fc_eohptr = DatumGetEOHP(fc_d);

	/* 返回内置的只读指针，而不是给定的指针 */
	return EOHPGetRODatum(fc_eohptr);
}

/*
 * 将扩展对象的所有权转移到一个新的父内存上下文。
 * 该对象必须由 R/W 指针引用，而我们返回的始终是它的“标准”R/W 指针，确保其生命周期与对象本身相同。 （传入的指针可能不是，并且在任何情况下，如果不是那个指针，就不会提供唯一标识符。）
 */
Datum TransferExpandedObject(Datum fc_d, MemoryContext fc_new_parent)
{
	ExpandedObjectHeader *fc_eohptr = DatumGetEOHP(fc_d);

	/* 断言调用者提供了一个读/写指针 */
	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(fc_d)));

	/* 转移所有权 */
	MemoryContextSetParent(fc_eohptr->eoh_context, fc_new_parent);

	/* 返回对象的标准读写指针 */
	return EOHPGetRWDatum(fc_eohptr);
}

/*
 * 删除一个扩展对象（必须由一个读/写指针引用）。
 */
void DeleteExpandedObject(Datum fc_d)
{
	ExpandedObjectHeader *fc_eohptr = DatumGetEOHP(fc_d);

	/* 断言调用者提供了一个读/写指针 */
	Assert(VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(fc_d)));

	/* Kill it */
	MemoryContextDelete(fc_eohptr->eoh_context);
}
